package scenic.path;

import java.awt.Rectangle;
import java.awt.geom.PathIterator;

import scenic.Util;
import scenic.geom.Affine2;
import scenic.geom.Double2;

/**
 * This class defines a path that can be used for stroking and filling.
 * A path consists of one or more subpaths. Each subpath consists
 * of one or more connected segments. There are different kinds 
 * of segments: straight lines, Bezier curves and elliptical arcs.
 * 
 * Path, subpaths and segments are constant. Their values cannot
 * be changed after they are created.
 * 
 */
public class Path {
	private SubPath[] subPaths;
	
	/**
	 * Creates a path that has the given subpaths.
	 * 
	 * @param subPaths the subpaths.
	 */
	public Path(SubPath[] subPaths) {
		this.subPaths = subPaths;
	}
	
	/**
	 * Creates a path from the given java.awt.Shape object.
	 * 
	 * @param shape the source path
	 */
	public Path(java.awt.Shape shape, Affine2 transform) {
		PathBuilder pathBuilder = new PathBuilder();
		PathIterator itr = shape.getPathIterator(transform.getAffineTransform());
		double[] point = new double[6];
		
		while(!itr.isDone()) {
			int type = itr.currentSegment(point);
			
			itr.next();
			if(type == PathIterator.SEG_MOVETO) {
				pathBuilder.moveTo(point[0], point[1]);
			} else if(type == PathIterator.SEG_LINETO) {
				pathBuilder.lineTo(point[0], point[1]);				
			} else if(type == PathIterator.SEG_QUADTO) {
				Double2 p1 = new Double2(point[0], point[1]);
				Double2 p2 = new Double2(point[2], point[3]);
				pathBuilder.curveTo(p1, p2);
			} else if(type == PathIterator.SEG_CUBICTO) {
				Double2 p1 = new Double2(point[0], point[1]);
				Double2 p2 = new Double2(point[2], point[3]);
				Double2 p3 = new Double2(point[4], point[5]);
				pathBuilder.curveTo(p1, p2, p3);
			} else if(type == PathIterator.SEG_CLOSE) {
				pathBuilder.close();
			}
		}
		
		subPaths = pathBuilder.getSubPaths();
	}
	
	/**
	 * Gets the subpaths.
	 */
	public SubPath[] getSubPaths() {
		return (SubPath[])subPaths.clone();
	}

	/**
	 * Walks the given walker through the path.
	 * 
	 * @param walker the walker.
	 * @param error the error matrix.
	 */
	public void walk(PathWalker walker, Affine2 error) {
		for(int i = 0; i < subPaths.length; i++)
			subPaths[i].walk(walker, error);
	}
	
	/**
	 * Calculates the bounds of this path using the given transform.
	 * 
	 * @param transform the transform.
	 * @return the bounds.
	 */
	public Rectangle getBounds(Affine2 transform) {
		Rectangle r = new Rectangle();
		
		for(int i = 0; i < subPaths.length; i++)
			subPaths[i].getBounds(transform, r);
		
		return r;
	}

	/**
	 * Helper method which modifies the rectangle so that it contains
	 * the given point when using the given transform.
	 * 
	 * @param tm the transform.
	 * @param r the rectangle.
	 * @param xp the x coordinate of the point.
	 * @param yp the y coordinate of the point.
	 */
	public static void addPointToBounds(Affine2 tm, Rectangle r, 
			double xp, double yp) {
		int xs = (int)Math.floor(tm.m00 * xp + tm.m01 * yp + tm.dx);
		int ys = (int)Math.floor(tm.m10 * xp + tm.m11 * yp + tm.dy);
		
		Util.combine(r, xs, ys);
	}
}
